<?php
namespace app\command\extend\http;

use app\command\UdpServer;
use app\common\model\Calculation;
use app\common\model\coin\CoinRuleRunTrade;
use mall\library\Huobi;
use think\facade\Db;

/**
 * 盈利报表处理
 * Class ApiCache
 * @package app\command\extend\http
 */
class ProfitStatistics extends Base{

    // 设置小数点保留数
    const SCALE = 20;

    // 日志类型
    const LOG_TYPE_LIST = [
        'ARRANGE'   => 'Arrange',
        'ONISSIONS' => 'omissions',
        'ARRANGE'   => 'Arrange',
    ];


    /**
     * 间隔时间执行，单位秒
     */
    const INTERVAL_TIME_LIST = [
        'EXE_RUN_WAIT'  => 2, //执行中等待时间（单位秒）
    ];

    /**
     * 设置 udp请求的接口
     */
    const TASK_CONFIG = [
        'CMD'   => 'API_CACHE_CALL',
        'IP'    => '127.0.0.1',
    ];


    /**
     * 返回类名
     */
    protected function getClassname()
    {
        // TODO: Implement getClassname() method.
        $classname = explode('\\',self::class);
        return array_pop($classname);
    }


    /**
     * 限制处理
     * @return mixed|\string[][][]
     */
    protected function getLimit()
    {
        // TODO: Implement getLimit() method.
        return [
            'Switch' => [    // 将单个计算加入报表
                'ip'        => ['127.0.0.1'],
                'params'    => ['type','id'],
            ],
            'Docking'   => [    // 执行后台运行
                'ip'    => ['127.0.0.1'],
                'params'=> ['params'],
            ],
        ];
    }

    /**
     * 初始化 内存 表
     * @return array|array[]|mixed
     */
    protected function getTableList()
    {
        // TODO: Implement getTableList() method.
        return [
            'CONFIG'   => ['config',true,true,1,[      //【表名,是否初始化创建,是否启用锁,记录数（1024的倍数）,字段参数】
                ['value','TYPE_STRING',20],  // 配置的值
            ]], //配置信息

            'JOIN'  =>  ['join',true,true,100,[    //需要插入报表的 trade id
                ['fixed','TYPE_INT',1],  // 固定值 1
            ]], //配置信息
        ];
    }

    /**
     * 配置表的键列表
     */
    const CONFIG_TABLE_KEY_LIST = [
            'EXECUTION_MARK'  => 'execution_mark',  // 执行标志，用于判断执行状态
    ];


    // 获取额外的错误类型列表 格式：['类型'=>'格式字符串']
    protected function getErrorTypeList() {
        return [
            'SWITCH_TYPE'   => 'Type [%s] does not exist',
        ];
    }

    /**
     * 添加日志
     * @param $type 日志类型
     * @param $message 日志信息
     */
    static function logSelf($type,$message) {
        return self::logStatic([
            'type'  => $type,
            'message'   => $message,
        ]);
    }

    /**
     * 请求转移
     * @param array $params
     * @return array
     * @throws \Exception
     */
    protected function callSwitch(array $params) {
        $result = [];
        switch ($params['type']) {
            case 'arrange': //整理久数据
                $this->task($result,'Arrange',$params);
                return $result;
            case 'omissions':   //处理遗漏
                $this->task($result,'omissions',$params);
                return $result;
            case 'insert':  // 插入数据
                return $this->switchInsertOne($params);
        }
        // 错误类型
        $this->error('SWITCH_TYPE',[$params['type']]);
    }

    /**
     * 整理历史数据
     * @param $params
     */
    static function taskArrange() {
        $coinRuleRunTradeModel = new CoinRuleRunTrade();
        list($del,$tradeWhere) = $coinRuleRunTradeModel::getStatusAndWhere('FINISH');
        $lastId = 0;
        $where = [];
        $where[] = ['type','=',$coinRuleRunTradeModel::TYPE_SELL];
        $where = array_merge($where,$tradeWhere);
        $where[] = ['statistics','=',$coinRuleRunTradeModel::JOIN_STATISTICS_LIST['NULL']];
        $fields = ['id','statistics','field_price','field_fees','field_quantity','reserved_quantity','version','run-unit_price','run-id','user_id'];
        $num = 0;
        // 循环获取需要处理的数据
        while($list = $coinRuleRunTradeModel->recordAll(array_merge($where,[['id','>',$lastId]]),[0,1000],['id'=>'ASC'],$fields)) {
            foreach($list as $tradeDetails) {
                try {
                    Db::startTrans();
                    // 设置计算
                    $tradeDetails['unit_price'] = $tradeDetails['run-unit_price'];
                    $profit = Calculation::profit($tradeDetails);
                    // 获取服务费
                    $serviceCharge = Calculation::serviceCharge(['profit'=>$profit],$tradeDetails['user_id']);
                    $update = [
                        'service_charge'    => $serviceCharge,
                        'statistics'    => bccomp($profit,0,Calculation::getScale('profit')) == 1?$coinRuleRunTradeModel::JOIN_STATISTICS_LIST['WAIT']:$coinRuleRunTradeModel::JOIN_STATISTICS_LIST['NOT_JOIN'],
                    ];

                    // 修改成功，添加日志
                    if($coinRuleRunTradeModel->orderUpdate($tradeDetails['id'],$update,$tradeDetails['version'])) {
                        self::logSelf(self::LOG_TYPE_LIST['ARRANGE'],['id' => $tradeDetails['id'], 'serviceCharge' => $serviceCharge]);
                        $num++;
                    }
                    Db::commit();
                } catch (\Exception $e) {
                    Db::rollback();
                    if($e->getCode() == -1) {
                        $e = [
                            'message'   => $e->getMessage(),
                        ];
                    }
                    self::logStatic($e,'ERROR');
                }
                $lastId = $tradeDetails['id'];
            }
        }
        if($num) {  // 添加整理遗漏
            self::taskOmissions();
        }
        return $num;
        // return $num . ':' . (microtime(true) - $time);
    }

    /**
     * 整理遗漏加紧报表
     */
    static function taskOmissions() {
        $coinRuleRunTradeModel = new CoinRuleRunTrade();
        $lastId = 0;
        $where = [];
        $where[] = ['statistics','=',$coinRuleRunTradeModel::JOIN_STATISTICS_LIST['WAIT']];
        $fields = ['id'];
        $num = 0;
        $joinTable =  self::swooleTable('JOIN');
        // 循环获取需要处理的数据
        while($list = $coinRuleRunTradeModel->recordAll(array_merge($where,[['id','>',$lastId]]),[0,1000],['id'=>'ASC'],$fields)) {
            foreach($list as $tradeDetails) {
                try {
                    // 将记录ID添加到执行中
                    $joinTable->set($tradeDetails['id'],['fixed'=>1]);
                    ++$num;
                } catch (\Exception $e) {
                    if($e->getCode() == -1) {
                        $e = [
                            'message'   => $e->getMessage(),
                        ];
                    }
                    self::logStatic($e,'ERROR');
                }
                $lastId = $tradeDetails['id'];
            }
        }
        if($num) {  // 执行处理
            self::execTask([
                'method'    => 'InsertOne',
                'params'    => [],
            ]);
        }
        return $num;
    }


    /**
     * 插入单条数据
     * @param array $params
     * @return array
     */
    protected function switchInsertOne(array $params) {
        $result = [];
        // 将记录ID添加到执行中
        self::swooleTable('JOIN')->set($params['id'],['fixed'=>1]);

        // 判断是否在执行
        $configTable = self::swooleTable('CONFIG');
        if(!($details = $configTable->get(self::CONFIG_TABLE_KEY_LIST['EXECUTION_MARK'])) ||
            bccomp(bcsub(microtime(true),explode('_',$details['value'])[0],4),self::INTERVAL_TIME_LIST['EXE_RUN_WAIT'],4) == 1
        ) {
            // 判断不存在执行中 或 已经执行超时重新开启后台执行
            $this->task($result,'InsertOne',[]);
        }

        return $result;
    }


    /**
     * 执行单个处理报表任务
     * @param $params
     */
    static function taskInsertOne($params) {
        $randId = rand(1000000,9999999);    // 执行唯一ID
        $configKey = self::CONFIG_TABLE_KEY_LIST['EXECUTION_MARK'];
        $configTable = self::swooleTable('CONFIG'); // 配置表
        $joinTable = self::swooleTable('JOIN'); //数据执行表
        $configTable->set($configKey,[
            'value' => microtime(true) . '_' . $randId
        ]);

        $isExit = false;
        while ($joinTable->count()) {
            foreach($joinTable as $tradeId=>$del) {
                // 循环执行处理数据
                self::tradeIntoStatistics($tradeId);

                // 删除历史
                $joinTable->del($tradeId);

                // 设置最新执行时间
                if(!$configTable->lockFunc(function($configTable) use($configKey,$randId){
                    if($details = $configTable->get($configKey)) {
                        $value = explode('_',$details['value']);
                        if($value[1] != $randId) {
                            return false;
                        }
                    }
                    // 设置
                    $configTable->set($configKey,[
                        'value' => microtime(true) . '_' . $randId
                    ]);
                    return true;
                })) {
                    $isExit = true; // 退出操作
                    break;
                }
            }
            if($isExit) {
                break;
            }
        }
        // 删除执行中状态
        $configTable->lockFunc(function($configTable) use($configKey,$randId){
            if($details = $configTable->get($configKey)) {
                $value = explode('_',$details['value']);
                if($value[1] == $randId) {
                    $configTable->del($configKey);
                    return false;
                }
            }
            return true;
        });
        return 'ok';
    }


    /**
     * 将记录添加到报表中
     * @param int $tradeId
     */
    static private function tradeIntoStatistics(int $tradeId) {
     //   $tradeId =  98387;
        try {
            Db::startTrans();
            $coinRuleRunTradeModel = new CoinRuleRunTrade();
            $where = [];
            $where[] = ['id','=',$tradeId];
            $where[] = ['statistics','=',$coinRuleRunTradeModel::JOIN_STATISTICS_LIST['WAIT']];
            $fields = ['id','statistics','field_price','field_fees','field_quantity','reserved_quantity','service_charge','service_charge_deducted','field_time','create_time','version','run-unit_price','run-id','user_id'];
            if(!$tradeDetails = $coinRuleRunTradeModel->recordAll($where,1,null,$fields)) {
                return false;
            }
            $tradeDetails['unit_price'] = $tradeDetails['run-unit_price'];
            $profitTime = $tradeDetails['field_time']?intval(bcdiv($tradeDetails['field_time'],1000)):$tradeDetails['create_time'];   // 获取盈利时间
            // 添加进报表
            (new \app\common\model\users\ProfitStatistics())->intoStatistics($tradeDetails,$tradeDetails['user_id'],$profitTime);

            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            if($e->getCode() == -1) {
                $e = [
                    'message'   => $e->getMessage(),
                ];
            }
            self::logStatic($e,'ERROR');
            return false;
        }
        return true;
    }

}